// $Id: array_traits.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_ARRAY_TRAITS_HPP
#define XPCOM_ARRAY_TRAITS_HPP

#include <xpcom/config.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/if.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_arithmetic.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/enable_if.hpp>
#include <xpcom/array_fwd.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/proxy/bool_ref.hpp>
#include <xpcom/proxy/pstring_ref.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

namespace xpcom {

namespace construct_policy {

struct ctor_construct_tag {};
struct void_construct_tag : ctor_construct_tag {};
struct zero_construct_tag : ctor_construct_tag {};

} // namespace xpcom::construct_policy

namespace destruct_policy {

struct dtor_destruct_tag {};
struct void_destruct_tag : dtor_destruct_tag {};

} // namespace xpcom::destruct_policy

// Base array traits for POD types (arithmetic, enum, pointers, C-style
// structures).
template <typename T, typename Init = boost::mpl::false_>
class pod_array_traits
{
public:
    typedef T           element_type;
    typedef T           value_type;
    typedef T*          pointer;
    typedef const T*    const_pointer;
    typedef T&          reference;
    typedef const T&    const_reference;

    typedef typename boost::mpl::if_<
        Init,
        construct_policy::zero_construct_tag,
        construct_policy::void_construct_tag
    >::type construct_policy_tag;
    typedef destruct_policy::void_destruct_tag destruct_policy_tag;

    static void construct(element_type& x)
    {
        if (Init::value)
            new(&x) T();
    }

    static void destruct(element_type& /*x*/)
    {
        // noop
    }

    static reference ref(element_type& x)
    {
        return x;
    }

    static const_reference const_ref(element_type& x)
    {
        return x;
    }
};

// Base array traits for classes.
// T must be default constructible.
template <typename T>
class class_array_traits
{
public:
    typedef T           element_type;
    typedef T           value_type;
    typedef T*          pointer;
    typedef const T*    const_pointer;
    typedef T&          reference;
    typedef const T&    const_reference;

    typedef construct_policy::ctor_construct_tag construct_policy_tag;
    typedef destruct_policy::dtor_destruct_tag destruct_policy_tag;

    static void construct(element_type& x)
    {
        new(&x) T();
    }

    static void destruct(element_type& x)
    {
        x.~T();
    }

    static reference ref(element_type& x)
    {
        return x;
    }

    static const_reference const_ref(element_type& x)
    {
        return x;
    }
};

// Base array traits for class pointers, and hides its pointer nature.
// T must be default constructible.
// Memory is allocated with XPCOM memory manager, since the array may go across
// modules.
template <typename T>
class pointer_array_traits
{
public:
    typedef T*          element_type;
    typedef T           value_type;
    typedef T*          pointer;
    typedef const T*    const_pointer;
    typedef T&          reference;
    typedef const T&    const_reference;

    typedef construct_policy::zero_construct_tag construct_policy_tag;
    typedef destruct_policy::dtor_destruct_tag destruct_policy_tag;

    static void construct(element_type& x)
    {
        x = 0; // allows null element, element is constructed at first access
    }

    static void destruct(element_type& x)
    {
        xpcom::checked_delete(x);
    }

    static reference ref(element_type& x)
    {
        ensure_instance(x);
        return *x;
    }

    static const_reference const_ref(element_type& x)
    {
        ensure_instance(x);
        return *x;
    }

private:
    static void ensure_instance(element_type& x)
    {
        if (!x)
            x = ::new(mem) T(); // default initialized
    }
};

// Base array traits for access from proxy types.
template <typename T, typename Proxy>
class proxy_array_traits
{
    BOOST_STATIC_ASSERT(sizeof(T) == sizeof(Proxy));
public:
    typedef T                   element_type;
    typedef Proxy               value_type;
    typedef value_type*         pointer;
    typedef const value_type*   const_pointer;
    typedef value_type&         reference;
    typedef const value_type&   const_reference;

    typedef construct_policy::ctor_construct_tag construct_policy_tag;
    typedef destruct_policy::dtor_destruct_tag destruct_policy_tag;

    static void construct(element_type& x)
    {
        new(&x) value_type();
    }

    static void destruct(element_type& x)
    {
        ref(x).~value_type();
    }

    static reference ref(element_type& x)
    {
        return *reinterpret_cast<value_type*>(&x);
    }

    static const_reference const_ref(element_type& x)
    {
        return *reinterpret_cast<const value_type*>(&x);
    }
};

//
// Primary array_traits template, suitable for all types.
// Requirements:
//  1. T is default constructible
//
template <typename T, typename Enable>
class array_traits :
    public class_array_traits<T>
{
};

//
// array_traits for pointer types.
// Requirements:
//  1. T is default constructible
//
template <typename T>
class array_traits<
        T, typename boost::enable_if<boost::is_pointer<T> >::type> :
    public pointer_array_traits<typename boost::remove_pointer<T>::type>
{
};

//
// array_traits for arithmetic types.
//
template <typename T>
class array_traits<
        T, typename boost::enable_if<boost::is_arithmetic<T> >::type> :
    public pod_array_traits<T>
{
};

//
// Explicit specializations of array_traits.
//
template <>
class array_traits<void*, void> :
    public pod_array_traits<void*, boost::mpl::true_>
{
};

template <>
class array_traits<bool, void> :
    public proxy_array_traits<bool_t, proxy::bool_ref>
{
public:
    typedef construct_policy::zero_construct_tag construct_policy_tag;
    typedef destruct_policy::void_destruct_tag destruct_policy_tag;
};

template <>
class array_traits<char*, void> :
    public proxy_array_traits<char*, proxy::pstring_ref<char> >
{
public:
    typedef construct_policy::zero_construct_tag construct_policy_tag;
};

template <>
class array_traits<wchar*, void> :
    public proxy_array_traits<wchar*, proxy::pstring_ref<wchar> >
{
public:
    typedef construct_policy::zero_construct_tag construct_policy_tag;
};

template <>
class array_traits<uuid, void> :
    public pointer_array_traits<uuid>
{
};

} // namespace xpcom

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_ARRAY_TRAITS_HPP
